#include "inode.h"
#include "logger.h"
#include <memory.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>

inode_t root_inode;

uint8_t data_bitmap[BLOCK_SIZE];
uint8_t data1_bitmap[BLOCK_SIZE];//需要2个data bitmap
uint8_t inode_bitmap[BLOCK_SIZE];//inode_bitmap占4KB 

//bitmap的pos比特置1
void set_bitmap(uint8_t* bm, uint16_t pos){
    bm[pos / 8] |= 1ULL << (pos % 8);
}

//bitmap的pos比特置0
void reset_bitmap(uint8_t* bm, uint16_t pos){
    bm[pos / 8] &= ~(1ULL << (pos % 8));
}

//获取bitmap的pos比特
uint8_t get_bitmap(uint8_t* bm, uint16_t pos){
    return (bm[pos / 8] >> (pos % 8)) & 1;
}

//写inode,和set_inode几乎一样,但是多一个检查功能
void write_inode(uint16_t inode_id, inode_t inode){
    //检查
    if(get_bitmap(inode_bitmap, inode_id)){
        fs_error("write inode failed, inode %d in use.\n", inode_id);
        return;
    }
    //计算block位置
    uint16_t block_id = FIRST_INODE_BLOCK + inode_id/INODE_PER_BLOCK;
    uint16_t offset = (inode_id % INODE_PER_BLOCK) * INODE_SIZE;
    block_t block;
    //先读取原有block内容
    disk_read(block_id,&block);
    //将inode内容写入到block对应位置
    //memcpy(block.data+offset, &inode, sizeof(inode_t));
    *(inode_t *)(block.data + offset) = inode;
    //写入到disk
    disk_write(block_id, &block);

    //更新bitmap
    set_bitmap(inode_bitmap,inode_id);
}

// 初始化根目录的inode
void init_root_inode() {
    fs_info("in init_root_inode\n");
    root_inode.is_dir = true;
    root_inode.size = 0;
    root_inode.indirect = 0;
    
    write_inode(0, root_inode);

    //调试用
    // mk("/hello",true);

}

// 根据编号返回inode_t
inode_t get_inode(uint16_t inode_id){
    uint16_t blk_id = FIRST_INODE_BLOCK + inode_id / INODE_PER_BLOCK;//块编号
	size_t offset = (inode_id % INODE_PER_BLOCK) * INODE_SIZE;//块中偏移量
	block_t blk;
	disk_read(blk_id, blk.data);

	return *(inode_t*)(blk.data + offset);
}

//获取inode下的目录项,第二个参数是目录项数组,返回目录数量
uint16_t get_dir_items(inode_t inode, file_t* items){
    if (!inode.is_dir) {
        // 如果当前 inode 不是目录，返回错误
        return 0xffff; // 返回0xffff表示失败
    }
    if(0 == inode.size && 0 == inode.indirect){
        return 0;//目录为空，返回0
    }

    uint16_t num_items = 0; // 已读取的目录项数量
    block_t block;
    uint16_t blk_num = inode.size/BLOCK_SIZE+1;

    block_t indirect_block;
    disk_read(inode.indirect, &indirect_block);

    for (int i = 0; i < blk_num; i++) {
        uint16_t blk_id = indirect_block.blk_id[i];
        if (0 == blk_id) {
            // 空块，跳过
            continue;
        }

        disk_read(blk_id, &block);

        for (int j = 0; j < FILE_PER_BLOCK; j++) {
            if (!block.file[j].is_dir) {
                // 非目录项，跳过
                continue;
            }
            items[num_items++] = block.file[j];
        }
    }


    return num_items; // 返回读取的目录项数量
}

//获取inode下的文件项,第二个参数是文件项数组,返回文件数量（包括目录）
uint16_t get_all_items(inode_t inode, file_t* items){
    if (!inode.is_dir) {
        // 如果当前 inode 不是目录，返回错误
        return 0xffff; // 返回0xffff表示失败
    }
    if(0 == inode.size && 0 == inode.indirect){
        return 0;//目录为空，返回0
    }

    uint16_t num_items = 0; // 已读取的目录项数量
    block_t block;
    uint16_t blk_num = inode.size/BLOCK_SIZE+1;

    block_t indirect_block;
    disk_read(inode.indirect, &indirect_block);

    for (int i = 0; i < blk_num; i++) {
        uint16_t blk_id = indirect_block.blk_id[i];
        if (0 == blk_id) {
            // 空块，跳过
            continue;
        }

        disk_read(blk_id, &block);

        for (int j = 0; j < FILE_PER_BLOCK; j++) {
            if( 0 == block.file[j].inode_id ){
                continue;
            }
            items[num_items++] = block.file[j];
        }
    }


    return num_items; // 返回读取的文件数量
}


//从父inode下找到所有文件，删除指定inode的信息
int rm_child_inode(inode_t inode, uint16_t child_id){
    if (!inode.is_dir) {
        // 如果当前 inode 不是目录，返回错误
        return -1; // 返回 -1 表示失败
    }
    if(0 == inode.size && 0 == inode.indirect){
        return -1;//目录为空，返回-1
    }

    uint16_t num_items = 0; // 已读取的目录项数量
    block_t block;
    uint16_t blk_num = inode.size/BLOCK_SIZE+1;
    bool found = false;
    block_t indirect_block;
    disk_read(inode.indirect, &indirect_block);

    for (int i = 0; i < blk_num; i++) {
        uint16_t blk_id = indirect_block.blk_id[i];
        if (0 == blk_id) {
            // 空块，跳过
            continue;
        }

        disk_read(blk_id, &block);

        for (int j = 0; j < FILE_PER_BLOCK; j++) {
            if( child_id != block.file[j].inode_id ){
                continue;
            }
            block.file[j].inode_id = 0;
            found =true;
            break;
        }
        if(found){
            //持久化
            disk_write(blk_id, &block);
            return 0;
        }
    }

    //没有找到
    return -1;
}

//释放指定id的inode的所有空间，包括数据空间和inode_bitmap空间
void release_inode(uint16_t id){
    inode_t inode = get_inode(id);

    if( 0 != inode.size && 0!=inode.indirect){

    }

    if(!get_bitmap(inode_bitmap, id)){
        reset_bitmap(inode_bitmap, id);
        disk_write(0, inode_bitmap);
    }
}


uint16_t new_inode(){
	for(uint16_t i = 1; i < TOTAL_INODES; ++i){
		if(!get_bitmap(inode_bitmap, i)){
			set_bitmap(inode_bitmap, i);
			disk_write(0, inode_bitmap);
			return i;
		}
    }
}

uint16_t new_block(){
	for(int i = FIRST_DATA_BLOCK; i < BLOCK_NUM/2; ++i){
		if(!get_bitmap(data_bitmap, i)){
			set_bitmap(data_bitmap, i);
			disk_write(1,data_bitmap);
			return i;
		}
    }
    for(int i = BLOCK_NUM/2; i <= BLOCK_NUM; ++i){
		if(!get_bitmap(data1_bitmap, i)){
			set_bitmap(data1_bitmap, i);
			disk_write(2,data1_bitmap);
			return i;
		}
    }
}

//将cur内容，写入inode_num对应的磁盘空间
void set_inode(inode_t cur, uint16_t inode_num){
	uint16_t blk_num = FIRST_INODE_BLOCK + inode_num / INODE_PER_BLOCK;
	size_t offset = (inode_num % INODE_PER_BLOCK) * INODE_SIZE;
	block_t blk;
	disk_read(blk_num, blk.data);
	*(inode_t *)(blk.data + offset) = cur;
	disk_write(blk_num, blk.data);
}

void init_inode(uint16_t inode_id, bool directory){
	inode_t tmp;
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
	tmp.atime = tmp.ctime = tmp.mtime = ts;
	tmp.size = 0;
	tmp.indirect = 0;
	tmp.is_dir = directory;
	set_inode(tmp,inode_id);
}

//改变inode占用空间
void resize_inode(inode_t* inode, uint32_t size){
    uint16_t cur_blk_num = inode->size ? inode->size/ BLOCK_SIZE+1:0;

    inode->size = size;
    if(inode->size && !inode->indirect){
        inode->indirect = new_block();
        block_t indirect_block;
        uint16_t* block_id = indirect_block.blk_id;
        block_id[0] = new_block();
        disk_write(inode->indirect,&indirect_block);
    }
    uint16_t need_blk_num = inode->size ? inode->size/ BLOCK_SIZE+1:0;
    if(cur_blk_num == need_blk_num){
        return;
    }
    block_t indirect_block;
    disk_read(inode->indirect, &indirect_block);
    //todo 考虑需要新增块的情况
    //todo 删除不需要的块空间
    if(cur_blk_num > need_blk_num){

        for (int i = 0; i < cur_blk_num; i++) {
            if(i<need_blk_num){
                continue;
            }
            int blk_id = indirect_block.blk_id[i];
            if(blk_id<BLOCK_NUM/2){
                reset_bitmap(data_bitmap,blk_id);
            }else{
                reset_bitmap(data1_bitmap,blk_id);
            }
        }

    }

}

//用文件数组来更新文件夹
void update_dir(inode_t* dir_inode, file_t *files, uint16_t file_cnt){
	block_t dir_blk; 
    uint16_t block_idx = 0;
    //扩容
    if(!dir_inode->indirect){
        dir_inode->indirect = new_block();
        block_t indirect_block;
        uint16_t* block_id = indirect_block.blk_id;
        block_id[0] = new_block();
        disk_write(dir_inode->indirect,&indirect_block);
    }


    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    dir_inode->atime = dir_inode->mtime = dir_inode->ctime = ts;
	disk_read(dir_inode->indirect, dir_blk.blk_id);

	uint16_t *blk_id = dir_blk.blk_id;

	char *ptr = (char *) files; 
	int num_of_blk = dir_inode->size/BLOCK_SIZE + 1;

	for(int i = 0; i < num_of_blk; ++i, ptr += BLOCK_SIZE){
        //将文件信息写入块,每次写一整块
        disk_write(blk_id[i], ptr);
    }
    //todo 删除不需要的块空间
}

//读取从 offset 开始的 size 字节内容到 buffer 中，但是不能超过 inode->size
// 4. 返回实际读取的字节数
int read_file(inode_t* inode, uint16_t inode_id, uint32_t size, int offset, char* buffer){
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    inode->atime = ts;
    // 更新 inode 的 atime
	set_inode(*inode,inode_id);

    if(inode->is_dir||0 == inode->indirect||0 == inode->size){
        return 0;
    }


    //读 blk_id
	block_t indirect_blk; 
	disk_read(inode->indirect, indirect_blk.blk_id);
    int block_idx = 0;

    //不能超过 inode->size
    if(offset + size > inode->size){
        size = inode->size - offset;
    }


    //跳过offset之前的内容
    uint32_t j = 0;
    while(j+BLOCK_SIZE<offset){
        j+=BLOCK_SIZE;
        block_idx++;
    }
    //distance 是buffer开始读的位置和data block首地址的距离
    int distance = offset - j;
    char tmp[BLOCK_SIZE]={0};
    disk_read(indirect_blk.blk_id[block_idx++], tmp);
    //BLOCK_SIZE - distance 是buffer从第一个要读的data block里读取的字节数
    memcpy(buffer,tmp+distance,BLOCK_SIZE - distance);

    //buffer+j-offset是buffer的偏移量
    for(j+=BLOCK_SIZE;j<offset+size;j+=BLOCK_SIZE,block_idx++){
        disk_read(indirect_blk.blk_id[block_idx], buffer+j-offset);
    }
    
    // 返回实际读取的字节数
    return size;
}

// 从 offset 开始写入 size 字节的内容到文件中
// 4. 返回实际写入的字节数
int write_file(inode_t* inode, uint16_t inode_id, uint32_t size, int offset, char* buffer){
    block_t indirect_blk;//间接块 
    int block_idx = 0;//间接块中元素的下标

    //如果文件为空,则先分配间接指针
    if(0 == inode->indirect){
        inode->indirect = new_block();
    }else{
        //先读出间接块内容
        disk_read(inode->indirect,indirect_blk.blk_id);
    }

    // 如果写入后的文件大小超过已经分配的数据块大小，新分配足够的数据块
    int curr_blk_num = inode->size / BLOCK_SIZE;
    if(inode->size%BLOCK_SIZE){
        curr_blk_num+=1;
    }

    int need_blk_num = (offset+size) / BLOCK_SIZE;
    if((offset+size) %BLOCK_SIZE){
        need_blk_num+=1;
    }

    
    if(curr_blk_num<need_blk_num){
        //分配新的数据块
        for(block_idx = curr_blk_num;block_idx<need_blk_num;block_idx++){
            indirect_blk.blk_id[block_idx] = new_block();
        }    
        disk_write(inode->indirect, &indirect_blk);
    }

    block_idx = 0;
    //跳过offset之前的内容
    uint32_t j = 0;
    while(j+BLOCK_SIZE<=offset){
        j+=BLOCK_SIZE;
        block_idx++;
    }
    //distance 是buffer开始写的位置和data block首地址的距离
    int distance = offset - j;
    char tmp[BLOCK_SIZE]={0};
    
    //先读出原本的块,填入新内容后，再写整个块
    disk_read(indirect_blk.blk_id[block_idx], tmp);
    //len 是buffer第一个要写的data block的字节数
    int len = (BLOCK_SIZE - distance) > size ? size : (BLOCK_SIZE - distance);
    memcpy(tmp+distance,buffer,len);
    disk_write(indirect_blk.blk_id[block_idx++], tmp);

    //buffer的偏移量
    for(j+=BLOCK_SIZE;j<offset+size;j+=BLOCK_SIZE,block_idx++){
        disk_write(indirect_blk.blk_id[block_idx], buffer+j-offset);
    }
    

    // 5. 更新 inode 的 mtime，ctime
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    inode->mtime = inode->ctime = ts;
    inode->size = offset + size;
	set_inode(*inode,inode_id);

    // 返回实际写入的字节数
    return size;
}

//path代表路径, directory代表是否为文件夹, new_inode_id 为新文件的inode_id，为0代表文件不存在
int mk(const char *path, bool directory, uint16_t new_inode_id){
    bool allocate = true;
	char name[25]={0};  
    uint16_t inode_id;
    char *lastSlash = strrchr(path, '/');
    static char parent_path[BLOCK_SIZE]={0};
    if(!parent_path){
        fs_error("路径字符串 %s 内存空间申请失败\n",path);
    }
    memcpy(parent_path, path, lastSlash-path);
    parent_path[lastSlash-path] = 0;
    memcpy(name, lastSlash+1, strlen(path) - strlen(parent_path));

    //father inode
	inode_t fa = get_inode_by_path(parent_path, &inode_id);
	static file_t ptr[FILE_PER_BLOCK*BLOCK_SIZE/2]; 
    memset(ptr,0,FILE_PER_BLOCK*BLOCK_SIZE/2);

	int cnt = get_all_items(fa,ptr);

    if(0 == new_inode_id){
        new_inode_id = new_inode();
    }else{
        allocate = false;
    }

    //遍历文件，检查是否存在重复的inode_id
    int i;
    for(i=0;i<cnt;i++){
        if(new_inode_id == ptr[i].inode_id){
            break;
        }
    }
    //没有找到文件，说明需要新增
    if(i==cnt){
        cnt++;
    }
    ptr[i].inode_id = new_inode_id;
    

    ptr[i].is_dir = directory;//文件也要设置is_dir字段

    memset(ptr[i].filename, 0, FILENAME_LEN);
	memcpy(ptr[i].filename, name, FILENAME_LEN);
    if(allocate){
	    init_inode(ptr[i].inode_id, directory);
	}else{
        set_inode(get_inode(ptr[i].inode_id),ptr[i].inode_id);
    }

    //更新父亲节点
    // fa.size = sizeof(file_t)*cnt;
	// update_dir(&fa, ptr, cnt);
    int offset = (cnt-1)*FILE_SIZE;
    if(allocate){
        write_file(&fa,inode_id,FILE_SIZE,offset,(char*)ptr+offset);
    }else{
	    update_dir(&fa, ptr, cnt);
    }    
    //更新后的节点要写回disk,持久化
    set_inode(fa, inode_id);

	return 0;
}

//删除文件或目录
// 1. 通过 path 找到其父目录的 inode，记作 parent_inode
// 2. 在 parent_inode 中删除该文件的 inode，记该 inode 为 child_inode
// 3. 遍历 child_inode 的 data_block 标记释放，最后标记释放 child_inode
// 4. 更新 parent_inode 的 mtime，ctime
// release 代表是否释放inode资源, 增加复用性, inode_id作为返回参数，存放了待删除文件的inode_id
int rm_file(const char *path,bool release,uint16_t* inode_id){
	char name[25]={0};  
    uint16_t fa_id,child_id;
	inode_t child = get_inode_by_path(path, &child_id);
    if(0xffff == child_id){
        // 文件不存在时返回 -ENOENT
        return -ENOENT;
    }
    //不释放资源时，需要获取inode_id
    if( !release && inode_id){
        *inode_id = child_id;
    }
    char parent_path[FILENAME_LEN+1]={0};
    char *lastSlash = strrchr(path, '/');
    memcpy(parent_path, path, lastSlash-path);

    //father inode
	inode_t fa = get_inode_by_path(parent_path, &fa_id);

    if(0xffff==fa_id){
        return -ENOENT;
    }

    rm_child_inode(fa,child_id);
    if(release){
        release_inode(child_id);
        resize_inode(&fa,fa.size-sizeof(file_t));
    }

    
    //更新后的节点要写回disk,持久化
    set_inode(fa, fa_id);

	return 0;
}


//根据路径获取inode,第二个参数修改inode_id,返回值是inode_t
inode_t get_inode_by_path(char* path,uint16_t*inode_id){
    inode_t cur_inode = get_inode(0); // 从根目录开始
    if (inode_id) *inode_id = 0;     // 初始化inode_id为根目录的inode编号
    while (*path) {
        // 跳过路径中的'/'
        while (*path == '/') {
            path++;
        }

        // 如果路径结束，返回当前inode
        if (!*path) break;

        if (!cur_inode.is_dir) {
            *inode_id = 0xffff;
            return (inode_t){0}; // 返回一个空的inode_t结构体表示错误
        }

        // 获取当前路径组件的长度
        int len = 0;
        while (path[len] && path[len] != '/') len++;

        // 获取当前目录中的所有目录项
        file_t dir_items[FILE_PER_BLOCK];
        int num_items = get_all_items(cur_inode, dir_items);

        // 在目录项中查找当前路径组件
        bool found = false;
        for (int i = 0; i < num_items; i++) {
            if (path == strstr(path, dir_items[i].filename) && len == strlen(dir_items[i].filename)) {
                // 找到匹配的目录项
                cur_inode = get_inode(dir_items[i].inode_id);
                if (inode_id) *inode_id = dir_items[i].inode_id;
                found = true;
                path += len; // 移动到下一个路径组件
                break;
            }
        }

        // 如果没有找到匹配的目录项，返回错误
        if (!found) {
            *inode_id = 0xffff;
            return (inode_t){0}; // 返回一个空的inode_t结构体表示错误
        }
    }

    return cur_inode; // 返回找到的inode
}